 /*
 
 File: Buffer.m
 
 Abstract: Implementation file for Buffer class. Contains code that
        creates an NSBitmapImageRep object to hold the screen bits.
        The NSBitmapImageRep is then used to create an NSImage to
        write the image to a TIFF file on disk.
 
 Version: 1.0
 
 Disclaimer: IMPORTANT:  This Apple software is supplied to you by 
 Apple Inc. ("Apple") in consideration of your agreement to the
 following terms, and your use, installation, modification or
 redistribution of this Apple software constitutes acceptance of these
 terms.  If you do not agree with these terms, please do not use,
 install, modify or redistribute this Apple software.
 
 In consideration of your agreement to abide by the following terms, and
 subject to these terms, Apple grants you a personal, non-exclusive
 license, under Apple's copyrights in this original Apple software (the
 "Apple Software"), to use, reproduce, modify and redistribute the Apple
 Software, with or without modifications, in source and/or binary forms;
 provided that if you redistribute the Apple Software in its entirety and
 without modifications, you must retain this notice and the following
 text and disclaimers in all such redistributions of the Apple Software. 
 Neither the name, trademarks, service marks or logos of Apple Inc. 
 may be used to endorse or promote products derived from the Apple
 Software without specific prior written permission from Apple.  Except
 as expressly stated in this notice, no other rights or licenses, express
 or implied, are granted by Apple herein, including but not limited to
 any patent rights that may be infringed by your derivative works or by
 other works in which the Apple Software may be incorporated.
 
 The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
 MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
 THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
 FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
 OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
 
 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
 MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
 AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
 STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
 
 Copyright (C) 2007 Apple Inc. All Rights Reserved.
 
 */ 

#import "Buffer.h"
#import "DisplayUtils.h"
#import "ImageUtils.h"

@interface Buffer (PrivateMethods)
-(void)flipImageData;
-(CGImageRef)createRGBImageFromBufferData;
@end

@implementation Buffer (PrivateMethods)

/*
  * perform an in-place swap from Quadrant 1 to Quadrant III format
  * (upside-down PostScript/GL to right side up QD/CG raster format)
  * We do this in-place, which requires more copying, but will touch
  * only half the pages.  (Display grabs are BIG!)
  *
  * Pixel reformatting may optionally be done here if needed.
  */
  
-(void)flipImageData
{
    long top, bottom;
    void * buffer;
    void * topP;
    void * bottomP;
    void * base;
    long rowBytes;

    top = 0;
    bottom = mHeight - 1;
    base = mData;
    rowBytes = [self bytesPerRow];
    buffer = malloc(rowBytes);
    NSAssert( buffer != nil, @"malloc failure");

    while ( top < bottom )
    {
        topP = (void *)((top * rowBytes) + (intptr_t)base);
        bottomP = (void *)((bottom * rowBytes) + (intptr_t)base);

        /*
          * Save and swap scanlines.
          *
          * This code does a simple in-place exchange with a temp buffer.
          * If you need to reformat the pixels, replace the first two bcopy()
          * calls with your own custom pixel reformatter.
          */
        bcopy( topP, buffer, rowBytes );
        bcopy( bottomP, topP, rowBytes );
        bcopy( buffer, bottomP, rowBytes );

        ++top;
        --bottom;
    }
    free( buffer );
}

// Create a RGB CGImageRef from our buffer data
-(CGImageRef)createRGBImageFromBufferData
{
    CGColorSpaceRef cSpace = CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB);
    NSAssert( cSpace != NULL, @"CGColorSpaceCreateWithName failure");

    CGContextRef bitmap = CGBitmapContextCreate(mData, mWidth, mHeight, 8, [self bytesPerRow],
                                    cSpace,  
	#if __BIG_ENDIAN__
            kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big /* XRGB Big Endian */);
	#else
             kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little /* XRGB Little Endian */);
	#endif                                    
    NSAssert( bitmap != NULL, @"CGBitmapContextCreate failure");

    // Get rid of color space
    CFRelease(cSpace);

    // Make an image out of our bitmap; does a cheap vm_copy of the  
    // bitmap
    CGImageRef image = CGBitmapContextCreateImage(bitmap);
    NSAssert( image != NULL, @"CGBitmapContextCreate failure");

    // Get rid of bitmap
    CFRelease(bitmap);
    
    return image;
}

@end

@implementation Buffer

- (id) init
{
	//Make sure client goes through designated initializer
	[self doesNotRecognizeSelector:_cmd];
	return nil;
}

// Create a buffer (NSBitmapImageRep) to hold the bits for 
// a full-screen display.
- (Buffer *) initFullScreenSizedBuffer
{
    if (self = [super init])
    {
        // Get display dimensions
        [DisplayUtils getMainDisplayDimensions:&mWidth displayHeight:&mHeight];

        mByteWidth = mWidth * 4;                // Assume 4 bytes/pixel for now
        mByteWidth = (mByteWidth + 3) & ~3;    // Align to 4 bytes

        mData = malloc(mByteWidth * mHeight);
        NSAssert( mData != 0, @"malloc failed");
    }
    return self;
}

// Returns the base address in memory of the actual 
// buffer from our bitmap image representation
-(void *)baseAddress
{
    return mData;
}

// Returns the bytes per row for the actual buffer in
// the bitmap image representation
-(long)bytesPerRow
{
    return mByteWidth;
}

// Create a TIFF file on the desktop from our data buffer
-(void)createTIFFImageFileOnDesktop
{
    // glReadPixels writes things from bottom to top, but we
    // need a top to bottom representation, so we must flip
    // the buffer contents.
    [self flipImageData];

    // Create a Quartz image from our pixel buffer bits
    CGImageRef imageRef = [self createRGBImageFromBufferData];
    NSAssert( imageRef != 0, @"cgImageFromPixelBuffer failed");

    // Make full pathname to the desktop directory
    NSString *desktopDirectory = nil;
    NSArray *paths = NSSearchPathForDirectoriesInDomains
                        (NSDesktopDirectory, NSUserDomainMask, YES);
    if ([paths count] > 0)  
    {
        desktopDirectory = [paths objectAtIndex:0];
    }

    NSMutableString *fullFilePathStr = [NSMutableString stringWithString:desktopDirectory];
    NSAssert( fullFilePathStr != nil, @"stringWithString failed");
    [fullFilePathStr appendString:@"/ScreenSnapshot.tiff"];

    NSString *finalPath = [NSString stringWithString:fullFilePathStr];
    NSAssert( finalPath != nil, @"stringWithString failed");

    CFURLRef url = CFURLCreateWithFileSystemPath (
                                                                kCFAllocatorDefault,
                                                                (CFStringRef)finalPath,
                                                                kCFURLPOSIXPathStyle,
                                                                false);
    NSAssert( url != 0, @"CFURLCreateWithFileSystemPath failed");
    // Save our screen bits to an image file on disk
    [ImageUtils createTIFFImageFileFromCGImage:imageRef  destFullPathURL:url];

    CGImageRelease(imageRef);
    CFRelease(url);
}


-(void)dealloc
{
     /* Get rid of data */
    free(mData);
    [super dealloc];
}

@end
